import os
import subprocess
import shlex
import re
from typing import List, Dict, Any, Optional, Union, Tuple
from src.utils.logger import log_info, log_error, log_ffmpeg
from src.modules.file_manager import FileManager

class FFmpegCommandBuilder:
    """
    FFmpeg 命令构建器 - 用于构建和执行复杂的 FFmpeg 命令
    支持链式调用、复杂滤镜构建和安全参数处理
    """
    
    def __init__(self, startupinfo=None, logger=None):
        """初始化 FFmpeg 命令构建器
        
        Args:
            startupinfo: Windows 下隐藏命令窗口的 startupinfo 对象
            logger: 日志记录函数
        """
        self.startupinfo = startupinfo
        self.logger = logger
        self.file_manager = FileManager(logger=logger)
        
        # 基本命令部分
        self.inputs = []  # 输入文件
        self.outputs = []  # 输出文件及其选项
        self.global_options = []  # 全局选项
        
        # 复杂滤镜
        self.video_filters = []  # 视频滤镜
        self.audio_filters = []  # 音频滤镜
        self.filter_complex = []  # 复杂滤镜图
        
        # 媒体流映射
        self.maps = []  # 流映射
        
        # 其他选项
        self.encoding_options = {
            'video': [],
            'audio': []
        }
    
    def _log(self, message, level="info"):
        """记录日志"""
        if self.logger:
            self.logger(message, level=level)
        elif level == "error":
            log_error(message)
        else:
            log_info(message)
    
    def add_input(self, input_path: str, options: List[str] = None) -> 'FFmpegCommandBuilder':
        """添加输入文件
        
        Args:
            input_path: 输入文件路径
            options: 输入文件特定选项
        
        Returns:
            self: 链式调用
        """
        self.inputs.append({
            'path': self._safe_path(input_path),
            'options': options or []
        })
        return self
    
    def add_output(self, output_path: str, overwrite: bool = True) -> 'FFmpegCommandBuilder':
        """添加输出文件
        
        Args:
            output_path: 输出文件路径
            overwrite: 是否覆盖现有文件
        
        Returns:
            self: 链式调用
        """
        options = ['-y'] if overwrite else []
        self.outputs.append({
            'path': self._safe_path(output_path),
            'options': options
        })
        return self
    
    def add_video_filter(self, filter_str: str) -> 'FFmpegCommandBuilder':
        """添加简单视频滤镜
        
        Args:
            filter_str: 视频滤镜表达式
        
        Returns:
            self: 链式调用
        """
        self.video_filters.append(filter_str)
        return self
    
    def add_audio_filter(self, filter_str: str) -> 'FFmpegCommandBuilder':
        """添加简单音频滤镜
        
        Args:
            filter_str: 音频滤镜表达式
        
        Returns:
            self: 链式调用
        """
        self.audio_filters.append(filter_str)
        return self
    
    def add_filter_complex(self, filter_str: str) -> 'FFmpegCommandBuilder':
        """添加复杂滤镜
        
        Args:
            filter_str: 复杂滤镜表达式
        
        Returns:
            self: 链式调用
        """
        self.filter_complex.append(filter_str)
        return self
    
    def add_map(self, map_str: str) -> 'FFmpegCommandBuilder':
        """添加流映射
        
        Args:
            map_str: 映射表达式，如 "0:v:0" 或 "[v]"
        
        Returns:
            self: 链式调用
        """
        self.maps.append(map_str)
        return self
    
    def set_video_codec(self, codec: str, preset: str = None, crf: int = None) -> 'FFmpegCommandBuilder':
        """设置视频编码器
        
        Args:
            codec: 编码器名称，如 "libx264"
            preset: 预设，如 "medium"
            crf: 恒定速率因子，视频质量参数

        Returns:
            self: 链式调用
        """
        self.encoding_options['video'] = ['-c:v', codec]
        if preset:
            self.encoding_options['video'].extend(['-preset', preset])
        if crf is not None:
            self.encoding_options['video'].extend(['-crf', str(crf)])
        return self
    
    def set_audio_codec(self, codec: str, bitrate: str = None) -> 'FFmpegCommandBuilder':
        """设置音频编码器
        
        Args:
            codec: 编码器名称，如 "aac"
            bitrate: 音频比特率，如 "192k"
          Returns:
            self: 链式调用
        """
        self.encoding_options['audio'] = ['-c:a', codec]
        if bitrate:
            self.encoding_options['audio'].extend(['-b:a', bitrate])
        return self
    
    def add_global_option(self, option: str, value: str = None) -> 'FFmpegCommandBuilder':
        """添加全局选项
          Args:            option: 选项名称
            value: 选项值
        
        Returns:
            self: 链式调用
        """
        self.global_options.append(option)
        if value is not None:
            self.global_options.append(value)
        return self
    
    def _safe_path(self, path: str) -> str:
        """确保路径安全可用于FFmpeg命令行，正确处理中文字符和特殊字符"""
        if not path or not isinstance(path, str):
            return path
        
        # 标准化路径 - 使用正斜杠
        normalized_path = path.replace('\\', '/')
        
        # 检查是否包含需要引用的字符（空格、中文字符、括号等）
        needs_quoting = (
            ' ' in normalized_path or 
            any(ord(c) > 127 for c in normalized_path) or  # 中文字符
            any(c in normalized_path for c in '&|<>()[]{}$;\'"`')  # 特殊字符，特别是括号
        )
        
        if needs_quoting:
            # 使用双引号包围路径，并转义内部的双引号
            escaped_path = normalized_path.replace('"', '\\"')
            return f'"{escaped_path}"'
        
        return normalized_path
    
    def build_command(self) -> List[str]:
        """构建完整的 FFmpeg 命令
        
        Returns:
            cmd: 完整的命令列表
        """
        cmd = ["ffmpeg"]
        
        # 添加所有输入 (输入文件必须在前面)
        for input_item in self.inputs:
            if input_item['options']:
                cmd.extend(input_item['options'])
            cmd.extend(["-i", input_item['path']])
        
        # 添加滤镜
        # 单一视频滤镜
        if self.video_filters:
            cmd.extend(["-vf", ",".join(self.video_filters)])
        
        # 单一音频滤镜
        if self.audio_filters:
            cmd.extend(["-af", ",".join(self.audio_filters)])
        
        # 复杂滤镜图
        if self.filter_complex:
            cmd.extend(["-filter_complex", ";".join(self.filter_complex)])
        
        # 流映射
        for map_item in self.maps:
            cmd.extend(["-map", map_item])
        
        # 编码选项
        if self.encoding_options['video']:
            cmd.extend(self.encoding_options['video'])
        
        if self.encoding_options['audio']:
            cmd.extend(self.encoding_options['audio'])
        
        # 添加全局选项 (放在编码选项之后)
        if self.global_options:
            cmd.extend(self.global_options)
        
        # 添加输出及其选项
        for output_item in self.outputs:
            if output_item['options']:
                cmd.extend(output_item['options'])
            cmd.append(output_item['path'])
        
        # 如果没有特别指定覆盖选项，默认添加
        if not any("-y" in output.get('options', []) for output in self.outputs):
            cmd.append("-y")
        
        return cmd
    
    def execute(self, operation_name="FFmpeg操作", check=True) -> dict:
        """执行构建的命令
        
        Args:
            operation_name: 操作描述，用于日志记录
            check: 是否检查返回码
        
        Returns:
            result: 执行结果字典，包含success、message、output_path等
        """
        cmd = self.build_command()
        cmd = ' '.join(cmd)
        self._log(f"执行 {operation_name}")
        
        try:
            # 确保输出目录存在
            for output_item in self.outputs:
                output_dir = os.path.dirname(output_item['path'].strip('"\''))
                if output_dir and not os.path.exists(output_dir):
                    os.makedirs(output_dir, exist_ok=True)
              # 执行命令
            # 在Windows上处理中文路径，使用UTF-8编码
            if os.name == 'nt':
                # Windows上使用utf-8编码处理中文路径
                process = subprocess.run(
                    cmd, 
                    startupinfo=self.startupinfo,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    shell=False,
                    encoding='utf-8',  # Windows上使用utf-8处理中文
                )
            else:
                # Linux/Unix系统
                process = subprocess.run(
                    cmd, 
                    startupinfo=self.startupinfo,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    universal_newlines=True,
                    encoding='utf-8',
                    check=False  # 自己处理错误
                )
            
            # 记录命令执行情况
            log_ffmpeg(cmd, operation_name, success=(process.returncode == 0), debug=True)
            
            if process.returncode == 0:
                self._log(f"{operation_name} 成功")
                output_path = self.outputs[-1]['path'].strip('"\'"') if self.outputs else None
                return {
                    "success": True, 
                    "message": f"{operation_name} 成功",
                    "output_path": output_path,
                    "stderr": process.stderr,
                    "stdout": process.stdout,
                    "returncode": process.returncode
                }
            else:
                error_message = self._extract_error_message(process.stderr)
                error_detail = f"{operation_name} 失败: {error_message}"
                self._log(error_detail, level="error")
                
                if check:
                    raise RuntimeError(error_detail)
                    
                return {
                    "success": False,
                    "message": error_detail,
                    "stderr": process.stderr,
                    "stdout": process.stdout,
                    "returncode": process.returncode
                }
                
        except Exception as e:
            error_detail = f"{operation_name} 异常: {str(e)}"
            self._log(error_detail, level="error")
            
            if check:
                raise RuntimeError(error_detail) from e
                
            return {
                "success": False,
                "message": error_detail,
                "exception": str(e)
            }
    
    def _extract_error_message(self, stderr_output: str) -> str:
        """从FFmpeg错误输出中提取关键错误信息"""
        if not stderr_output:
            return "未知错误"
            
        # 查找错误行
        error_lines = re.findall(r'(?i)error.*', stderr_output)
        if error_lines:
            return error_lines[0].strip()
        
        # 如果没有明确的错误标记，则返回最后几行
        lines = stderr_output.strip().split('\n')
        if lines:
            return lines[-1].strip()
            
        return stderr_output[:200] + "..." if len(stderr_output) > 200 else stderr_output
